supce's blog

CSS Secret 读书笔记之染色效果&毛玻璃效果


染色效果

有时候会遇到这样一种场景:需要给图片增加染色效果,当发生hover或者交互时再把染色效果去除。
最容易想到的是以下两种方式:

  • 把图片做成两个版本,然后用CSS使得两个版本交替呈现。这种方式会导致更大的文件体积和额外的HTTP请求,同时也不便于维护(想改变染色主色调时需要重新制作图片)。
  • 还有一种方式,可以在图片上方覆盖一层半透明的纯色或者把图片设置为半透明并覆盖在一层纯色背景之上。这种方式会降低图片的对比度。

为了避免上面两种方式带来的问题,可以用下面这两种纯CSS的方式解决

基于滤镜的方案

要想给图片染色,这里用到了三个滤镜,分别式sepia()saturate()hue-rotate()

  • sepia会增加一种将饱和度的橙黄色染色特效,这种特效会将图片复古成黑白老照片。
  • saturate会改变像素的饱和度。
  • hue-rotate会把每个像素的色相以指定的度数进行偏移。

当然,给图片染色还可以用grayscalecontrast等。

比如下面的例子给图片增加一种暖色染色效果,出发hover时恢复原图:
HTML:

<div class="test-a">
    <img src="test.jpg">
</div>

CSS:

body{
    display: flex;
    flex-flow: row wrap;
    justify-content: space-around;
}
div{
    margin: 20px;
    width: 200px;
    height: 200px;
}
img{
    max-width: 100%;
}
.test-a > img{
    -webkit-filter: sepia(1) saturate(3) hue-rotate(300deg);
    filter: sepia(1) saturate(3) hue-rotate(300deg);
    /*transition: 5s -webkit-filter;*/
}
.test-a>img:hover,.test-a>img:focus{
    -webkit-filter: none;
    filter: none;
}

基于混合模式的方案

滤镜方案虽然可以染色,但是有一定的限制,它比较适合染褪色之类的效果。如果想消除这种褪色的染色效果,可以使用saturate增加饱和度,但是又会得到一种不自然的、过渡风格化的效果。这时候可以利用混合模式。

混合模式是指,当两个元素叠加时,该模式控制了上层元素的颜色与下层颜色进行混合。用它来实现染色效果时,需要用到的混合模式是luminosity、这种luminosity的混合模式会保留上层元素的HSL亮度信息,并从它的下层吸取色相和饱和度信息。这时候只需要将下层设置为想要的主色调,并把图片放在上层即可。

可以用两种方式实现混合模式:
第一种是把图片包裹在一个容器里,并把容器的背景色设置为需要的背景色。
第二种是不用img标签,而是使用div,把div第一层背景设置为要染色的图片,第二层设置为想要的主色调。
HTML:

<div class="test-b">
    <img src="test.jpg">
</div>

CSS:

.test-b{
    background-color: #655;
}
.test-b>img{
    mix-blend-mode: luminosity;
}

因为mix-blend-mode是把整个元素向下进行混合,而不管它的下层是什么。因此第一种方式无法使用动画效果对图片进行还原。如果使用第二种方式,使用background-blend-mode属性,可以让每层背景跟它的下层背景混合,并且不关心元素之外的颜色。如果设置主色调为透明,就会还原到原来的图片。这样就可以使用动画对图片进行还原了。

<div class="test-c"></div>
.test-c{
    background-image: url(test.jpg);
    background-size: cover;
    background-color: #655;
    background-blend-mode: luminosity;
    transition: 3s background-color;
}
.test-c:hover{
    background-color: transparent;
}


毛玻璃效果

半透明作为某个元素的背景,把它放在一张照片或者花哨的背景之上,是一种很美的设计效果,但是可能会导致文字比较难以阅读。比如下面这种情况:
HTML:

<div class="blur-a">
    <blockquote>
        “The only way to get rid of a temptation is to yield to it. Resist it, and your soul grows sick with longing for the things it has forbidden to itself, with desire for what its monstrous laws have made monstrous and unlawful.”
        <cite>— Oscar Wilde, The Picture of Dorian Gray</cite>
    </blockquote>
</div>

CSS:

.blur-a{
    width: 40em;
    height: 20em;
    background: url(tiger.jpg);
    background-size: cover;
    display: flex;
}
.blur-a>blockquote{
    margin: auto;
    padding: 1em;
    width: 80%;height: 70%;
    font: italic bold 140%/140% sans-serif;
    background: hsla(0,0%,100%,.3);
    /*-webkit-filter:blur(2px);*/
    border-radius: .3em;
    box-shadow: 0 0 0 1px hsla(0,0%,100%,.3) inset,
        0 .5em 1em rgba(0, 0, 0, 0.6);
    text-shadow: 0 1px 1px hsla(0,0%,100%,.3);
}

这个时候可以使用模糊滤镜(filter:blur(2px))产生一种毛玻璃的效果:

结果发现文字也被模糊了,如果想要不模糊文字,同时又有毛玻璃效果就可以使用下面这种方式。
首先要求背景图片的background-attachment为fixed,然后设置一个伪元素,将其定位到元素的下层,再对伪元素进行模糊。
HTML:

<div class="blur-b">
    <blockquote>
        “The only way to get rid of a temptation is to yield to it. Resist it, and your soul grows sick with longing for the things it has forbidden to itself, with desire for what its monstrous laws have made monstrous and unlawful.”
        <cite>— Oscar Wilde, The Picture of Dorian Gray</cite>
    </blockquote>
</div>

CSS:

.blur-b{
    z-index: -2;
}
.blur-b>blockquote{
    position: relative;
    margin: auto;
    padding: 1em;
    width: 80%;height: 70%;
    font: italic bold 140%/140% sans-serif;
    overflow: hidden;
    border-radius: .3em;
    box-shadow: 0 0 0 1px hsla(0,0%,100%,.3) inset,
        0 .5em 1em rgba(0, 0, 0, 0.6);
    text-shadow: 0 1px 1px hsla(0,0%,100%,.3);
}
.blur-b>blockquote::before{
    content: "";
    position: absolute;
    top: 0;right: 0; bottom: 0;left: 0;
    background: url(tiger.jpg) 0 / cover fixed;
    z-index: -1;
    -webkit-filter: blur(10px);
    filter: blur(10px);
    margin: -30px;
}

这里有个坑,就是在背景图不是fixed的情况下使用毛玻璃,以后会填上的。